use std::sync::Arc;

use axum::{
    extract::{Json as ExtractJson, State},
    http::StatusCode,
    response::IntoResponse,
};
use serde::{Deserialize, Serialize};
use tracing::{debug, warn};

use crate::AppState;
use bc_base::{PublicKeyHashBytes, TransactionState, TransactionValue};
use bc_manager::SendTransactionRequest;

#[derive(Deserialize, Debug)]
pub(crate) struct TransactionRequest {
    // sender: String, // PublicKeyHashBytes
    // private_key: String,
    receiver: String, // PublicKeyHashBytes
    amount: TransactionValue,
}

#[derive(Serialize)]
struct TransactionResponse {
    tx_id: String,
    status: String,
}

//
// curl -X POST http://localhost:3000/api/transaction/send -H "Content-Type: application/json" \
// -d '{"sender": "12", "private_key": "34", "receiver": "56", "amount": 78}'
//
pub(crate) async fn send_transaction(
    State(state): State<Arc<AppState>>,
    ExtractJson(request): ExtractJson<TransactionRequest>,
) -> impl IntoResponse {
    debug!("send_transaction. body: {:?}", request);

    if request.amount == 0 {
        let response = TransactionResponse {
            tx_id: "amount can not be zero".to_string(),
            status: TransactionState::NotCreated.to_string(),
        };

        return (StatusCode::BAD_REQUEST, ExtractJson(response));
    }

    // let sender = PublicKeyHashBytes::from_hex_str(&request.sender);
    let recipient = PublicKeyHashBytes::from_hex_str(&request.receiver);

    // if sender.is_err() || recipient.is_err() {
    if recipient.is_err() {
        let response = TransactionResponse {
            tx_id: "Receiver are invalid public-key-hash format".to_string(),
            status: TransactionState::NotCreated.to_string(),
        };

        return (StatusCode::BAD_REQUEST, ExtractJson(response));
    }

    // let sender = sender.unwrap();
    let recipient = recipient.unwrap();
    let req = SendTransactionRequest::new(recipient, request.amount);

    let mng = state.bc_manager.lock().await;
    let r = mng.send_transaction(req).await;
    match r {
        Ok((tx_id, state)) => {
            let response = TransactionResponse {
                tx_id: tx_id.to_string(),
                status: state.to_string(),
            };

            (StatusCode::OK, ExtractJson(response))
        }
        Err(e) => {
            warn!("[send_transaction failed] {}", e);
            let err_str = format!("[Failed] {}", e);
            let response = TransactionResponse {
                tx_id: err_str,
                status: TransactionState::NotCreated.to_string(),
            };

            (StatusCode::BAD_REQUEST, ExtractJson(response))
        }
    }
}
